﻿using System;
using System.Collections.Generic;
using Microsoft.Practices.Unity;
using WDA.Config;
using System.Linq;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace WDA.Core
{
	/// <summary>
	/// WDAB runtime component - the core of WDAB application. Runtime component delegates functions to so-called
	/// modules (<see cref="IModule"/>). Module instances are supplied by subclasses of <see cref="RuntimeBase"/>.	
	/// </summary>
	/// <remarks>
	/// Module instance creation will be moved to RuntimeBase in future. 
	/// </remarks>
	public abstract class RuntimeBase : IDisposable
	{
		private readonly Dictionary<Type, IServiceController> _serviceControllers = new Dictionary<Type, IServiceController>();
		private readonly List<IModule> _modules = new List<IModule>();
		protected bool _disposed;
		
		private static readonly Logger _logger = new Logger("WDA.Core.HostRuntime");

		/// <summary>
		/// Inicjuje środowisko urochomieniowe. Konfiguruje hierarchię kontenerów. Tworzy i inicjuje moduły.
		/// Tworzy kontrolery serwisów w modułach.
		/// </summary>
		/// <param name="outerContainer">Zewnętrzny kontener Unity (konfigurujący).</param>
		/// <param name="persistentContainer">Twrały kontener Unity.</param>
		/// <param name="configName">Nazwa konfiguracji środowiska.</param>
		/// <param name="hostName">Nazwa hosta - nazwa środowiska.</param>
		/// <param name="addAsChildContainer">Czy tworzyć główny kontener jako potomka kontenera zewnętrznego.</param>
		public void Init(IUnityContainer outerContainer, IUnityContainer persistentContainer, string configName, string hostName, bool addAsChildContainer)
		{
			_logger.WriteInfo("Starting up WDA runtime...");

		   IUnityContainer localContainer = addAsChildContainer ? outerContainer.CreateChildContainer() : new UnityContainer();
			localContainer.RegisterInstance(localContainer);
			localContainer.RegisterInstance(WDAContainerNames.InternalContainerName, localContainer);
			localContainer.RegisterInstance(WDAContainerNames.PersistentInternalContainerName, persistentContainer);
			persistentContainer.RegisterInstance(WDAContainerNames.InternalContainerName, localContainer);

			localContainer.AddNewStrategy<BuilderAwareStrategy>(UnityBuildStage.PostInitialization);

			_modules.AddRange(CreateModules());

         IConfigurationManager configManager = outerContainer.Resolve<IConfigurationManager>();
         IConfiguration config = configManager.GetConfiguration(configName, true);                  

			foreach (IModule module in _modules)
			{
				module.Init(outerContainer, localContainer, persistentContainer, configName, hostName);
			}			

			foreach (IModule module in _modules)
			{
				module.CreateServiceControllers(config, _serviceControllers);
			}

			_logger.WriteInfo("WDA runtime started.");
		}

		/// <summary>
		/// Tworzy moduły komponentu uruchomieniowego specyficzne dla danego zastosowania.
		/// </summary>
		/// <returns></returns>
		protected abstract IEnumerable<IModule> CreateModules();		

		/// <summary>
		/// Zwraca referencję do proxy usługi korzystając z kontrolerów usług utworzonych przez moduły.
		/// </summary>
		/// <param name="serviceType">Typ CLR kontraktu usługi.</param>
		/// <returns>Obiekt proxy usługi.</returns>
		public object Get(Type serviceType)
		{
			IServiceController ctrl;
			if (_serviceControllers.TryGetValue(serviceType, out ctrl))
			{
				return ctrl.CreateServiceChannel();
			}
		   throw new ArgumentException("Service type not registered.");
		}				

		#region IDisposable Members
		public void Dispose()
		{
			Dispose(true);
		}
		public virtual void Dispose(bool disposing)
		{
			if (!_disposed)
			{
				if (disposing)
				{					
					foreach (IModule module in _modules)
					{
						module.Dispose();
					}
               foreach (IServiceController ctrl in _serviceControllers.Values)
               {
                  ctrl.Dispose();
               }
					_disposed = true;
				}
			}
			GC.SuppressFinalize(this);
		}
		#endregion
	}
}
